﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Activities;
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Workflow;
using System.IO;
using System.Net;
using System.Data;
using System.Globalization;


namespace AnjepIT.GetGeoCode
{
    public class GetGeoCodeActivity : CodeActivity
    {
        /// <summary>
        /// Input parameter of the address steet 1. This will be passed to google map API as first parameter.
        /// </summary>
        [Input("Address street 1")]
        public InArgument<string> Address1 { get; set; }

        /// <summary>
        /// Input parameter of the adress street 2.
        /// </summary>
        [Input("Address street 2")]
        public InArgument<string> Address2 { get; set; }

        /// <summary>
        /// Input parameter of the adress street 3.
        /// </summary>
        [Input("Address street 3")]
        public InArgument<string> Address3 { get; set; }

        /// <summary>
        /// Input Parameter of the postal.
        /// </summary>
        [Input("Postal")]
        public InArgument<string> Postal { get; set; }

        /// <summary>
        /// Input Parameter of the city.
        /// </summary>
        [Input("City")]
        public InArgument<string> City { get; set; }

        /// <summary>
        /// Input Parameter of the State.
        /// </summary>
        [Input("State")]
        public InArgument<string> State { get; set; }

        /// <summary>
        /// Input Parameter of Country.
        /// </summary>
        [Input("Country")]
        public InArgument<string> Country { get; set; }

        /// <summary>
        /// Output Parameter containing Longitude value of the address input given from entity.
        /// </summary>
        [Output("Longitude")]
        public OutArgument<double> Longitude { get; set; }

        /// <summary>
        /// Output Parameter containing Latitude value of the address input given from entity.
        /// </summary>
        [Output("Latitude")]
        public OutArgument<double> Latitude { get; set; }

        /// <summary>
        /// Execute method of the code activity to execute default.
        /// </summary>
        /// <param name="executionContext">Execution context of the workflow.</param>
        protected override void Execute(CodeActivityContext executionContext)
        {
            ITracingService tracingService = executionContext.GetExtension<ITracingService>();
            IWorkflowContext context = executionContext.GetExtension<IWorkflowContext>();
            IOrganizationServiceFactory serviceFactory = executionContext.GetExtension<IOrganizationServiceFactory>();
            IOrganizationService service = serviceFactory.CreateOrganizationService(context.UserId);

            String entityName = context.PrimaryEntityName;
            Guid entityId = context.PrimaryEntityId;

            string address = GetAddress(executionContext);
            Tuple<float, float> str = GetGeoCodeFromGoogleMap(address);

            Longitude.Set(executionContext, str.Item1);
            Latitude.Set(executionContext, str.Item2);
        }

        /// <summary>
        /// Get Address information from input parameters and concat in single string address.
        /// </summary>
        /// <param name="executionContext">Execution context of the workflow passed by CRM.</param>
        /// <returns>Address information extracted from input parameter and pass to google map.</returns>
        private string GetAddress(CodeActivityContext executionContext)
        {
            string address = string.Empty;
            if (Address1 != null)
                address += Address1.Get<string>(executionContext) + ", ";

            if (Address2 != null)
                address += Address2.Get<string>(executionContext) + ", ";

            if (Address3 != null)
                address += Address3.Get<string>(executionContext) + ", ";

            if (Postal != null)
                address += Postal.Get<string>(executionContext) + ", ";

            if (City != null)
                address += City.Get<string>(executionContext) + ", ";

            if (State != null)
                address += State.Get<string>(executionContext) + ", ";

            if (Country != null)
                address += Country.Get<string>(executionContext);

            address = address.Trim();

            if (address.EndsWith(","))
                address = address.Substring(0, address.Length - 1);

            return address;
        }
         
        /// <summary>
        /// Call google map API and pass address information in it.
        /// </summary>
        /// <param name="address">Address information, to be passed to the google map API.</param>
        /// <returns>Longitude and Latitude respectively.</returns>
        public Tuple<float, float> GetGeoCodeFromGoogleMap(string address)
        {
            float lat = 0, lng = 0;
            string url = "http://maps.google.com/maps/api/geocode/xml?address=" + address + "&sensor=false";
            WebRequest request = WebRequest.Create(url);
            using (WebResponse response = (HttpWebResponse)request.GetResponse())
            {
                using (StreamReader reader = new StreamReader(response.GetResponseStream(), Encoding.UTF8))
                {
                    DataSet dsResult = new DataSet();
                    dsResult.ReadXml(reader);
                    if (dsResult.Tables.Contains("result") && dsResult.Tables.Contains("location") && dsResult.Tables.Contains("geometry"))
                    {
                        foreach (DataRow row in dsResult.Tables["result"].Rows)
                        {
                            string geometry_id = dsResult.Tables["geometry"].Select("result_id = " + row["result_id"].ToString())[0]["geometry_id"].ToString();
                            DataRow location = dsResult.Tables["location"].Select("geometry_id = " + geometry_id)[0];
                            
                            lat = float.Parse(Convert.ToString(location["lat"]), System.Globalization.CultureInfo.InvariantCulture);
                            lng = float.Parse(Convert.ToString(location["lng"]), System.Globalization.CultureInfo.InvariantCulture);
                        }
                    }
                }
            }
            return new Tuple<float, float>(lng, lat);
        }
    }
}
